////////////////////////////////////////////////////////////////////////////
// Portions Copyright (c) 2010-2011 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// LCD Driver for MARY OLED (http://www.marutsu.co.jp/shohin_104614/)
////////////////////////////////////////////////////////////////////////////

#include "tinyhal.h"
#include "MARY_OLED.h"
#include "font6x8.h"

void SPISW_Initialize(void);
void SPISW_LCD_cmd(UINT8 dat);
void SPISW_LCD_dat(UINT8 dat);
void SPISW_LCD_on(void);

#define C_SET_COLUMN_ADDRESS 0x15
#define C_WRITE_RAM_COMMAND 0x5c
#define C_READ_RAM_COMMAND  0x5d
#define C_SET_ROW_ADDRESS    0x75
#define C_HORIZONTAL_SCROLL 0x96
#define C_STOP_MOVING  0x009e
#define C_START_MOVING 0x009f
#define C_SET_REMAP_COLOR_DEPTH 0xa0
#define C_SET_DISPLAY_START_LINE 0xa1
#define C_SET_DISPLAY_OFFSET 0xa2
#define C_SET_DISPLAY_MODE_ALL_OFF 0xa4
#define C_SET_DISPLAY_MODE_ALL_ON  0xa5
#define C_SET_DISPLAY_MODE_RESET   0xa6
#define C_SET_DISPLAY_MODE_INVERSE 0xa7
#define C_FUNCTION_SELECTION 0xab
#define C_SET_SLEEP_MODE_ON  0xae
#define C_SET_SLEEP_MODE_OFF 0xaf
#define C_SET_RESET_PRECHARGE_PERIOD 0xb1
#define C_ENHANCE_DRIVING_SCHEME_CAPABILITY 0xb2
#define C_FRONT_CLOCK_DRIVER_OSCILLATOR_FREQUENCY 0xb3
#define C_SET_SEGMENT_LOW_VOLTAGE 0xb4
#define C_SET_GPIO 0xb5
#define C_SET_SECOND_PRECHARGE_PERIOD 0xb6
#define C_LOOKUP_TABLE_FOR_GRAY_SCALE_PULSE_WIDTH 0xb8
#define C_USE_BUILT_IN_LINEAR_LUT 0xb9
#define C_SET_PRECHARGE_VOLTAGE 0xbb
#define C_SET_VCOMH_VOLTAGE 0xbe
#define C_SET_CONTRAST_CURRENT_FOR_COLOR_ABC 0xc1
#define C_MASTER_CONTRAST_CURRENT_CONTROL 0xc7
#define C_SET_MUX_RATIO 0xca
#define C_SET_COMMAND_LOCK 0xfd

static const UINT8 GAMMA_TABLE[63] =
{
    0x02, 0x03, 0x04, 0x05,
    0x06, 0x07, 0x08, 0x09,
    0x0a, 0x0b, 0x0c, 0x0d,
    0x0e, 0x0f, 0x10, 0x11,
    //
    0x12, 0x13, 0x15, 0x17,
    0x19, 0x1b, 0x1d, 0x1f,
    0x21, 0x23, 0x25, 0x27,
    0x2a, 0x2d, 0x30, 0x33,
    //
    0x36, 0x39, 0x3c, 0x3f,
    0x42, 0x45, 0x48, 0x4c,
    0x50, 0x54, 0x58, 0x5c,
    0x60, 0x64, 0x68, 0x6c,
    //
    0x70, 0x74, 0x78, 0x7d,
    0x82, 0x87, 0x8c, 0x91,
    0x96, 0x9b, 0xa0, 0xa5,
    0xaa, 0xaf, 0xb4
};

#define MARY_OLED_PWX   128
#define MARY_OLED_PWY   128
#define MARY_OLED_SX    0
#define MARY_OLED_SY    0
#define MARY_OLED_WX    128
#define MARY_OLED_WY    128
#define MARY_OLED_BITSPERPIXEL  12
#define MARY_OLED_FCOL  0xFFFFFF
#define MARY_OLED_BCOL  0x000000

#define FONT_WX     6
#define FONT_WY     8
#define TEXT_SX     MARY_OLED_SX
#define TEXT_SY     MARY_OLED_SY
#define LINE_WX     (MARY_OLED_WX/FONT_WX)
#define LINE_WY     (MARY_OLED_WY/FONT_WY)
//#define SCROLL_SY     1       // screen top
//#define SCROLL_EY    31       // screen bottom
//#define SCROLL_WY    29       // screen width
//#define SCROLL_FLAG   0       // bottom scroll

int cx = 0;                    // character x position
int cy = 0;                    // character y position
#ifdef SCROLL
int sy = 0;                    // scroll start y
int scroll = false;            // scroll on off flag
#endif

static void delay_ms(volatile UINT32 n)
{
    HAL_Time_Sleep_MicroSeconds_InterruptEnabled(n * 1000);
}

// MARY1
// OLED_VCC_ON  CN2-1    P46 AN6
// OLED_RESN    CN3-1    P92
// OLED_CNS     CN3-2    PC1
// OLED_SCK     CN4-2    PC5
// OLED_SDIN    CN4-4    PC6

void MARY_OLED_Initialize()
{
    int i;
    SPISW_Initialize();
    delay_ms(200);
    SPISW_LCD_cmd(C_SET_DISPLAY_MODE_ALL_OFF);
    SPISW_LCD_cmd(C_SET_COMMAND_LOCK);
    SPISW_LCD_dat(0x12);
    SPISW_LCD_cmd(C_SET_COMMAND_LOCK);
    SPISW_LCD_dat(0xb1);
    SPISW_LCD_cmd(C_SET_SLEEP_MODE_ON);
    SPISW_LCD_cmd(C_FRONT_CLOCK_DRIVER_OSCILLATOR_FREQUENCY);
    SPISW_LCD_dat(0xf1);
    SPISW_LCD_cmd(C_SET_MUX_RATIO);
    SPISW_LCD_dat(0x7f);
    SPISW_LCD_cmd(C_SET_DISPLAY_OFFSET);
    SPISW_LCD_dat(0x00);
    SPISW_LCD_cmd(C_SET_DISPLAY_START_LINE);
    SPISW_LCD_dat(0x00);
    SPISW_LCD_cmd(C_SET_REMAP_COLOR_DEPTH);
    SPISW_LCD_dat(0x74);
    SPISW_LCD_cmd(C_SET_GPIO);
    SPISW_LCD_dat(0x00);
    SPISW_LCD_cmd(C_FUNCTION_SELECTION);
    SPISW_LCD_dat(0x01);
    SPISW_LCD_cmd(C_SET_SEGMENT_LOW_VOLTAGE);
    SPISW_LCD_dat(0xa0);
    SPISW_LCD_dat(0xb5);
    SPISW_LCD_dat(0x55);
    SPISW_LCD_cmd(C_SET_CONTRAST_CURRENT_FOR_COLOR_ABC);
    SPISW_LCD_dat(0xc8);
    SPISW_LCD_dat(0x80);
    SPISW_LCD_dat(0xc8);
    SPISW_LCD_cmd(C_MASTER_CONTRAST_CURRENT_CONTROL);
    SPISW_LCD_dat(0x0f);
    SPISW_LCD_cmd(C_LOOKUP_TABLE_FOR_GRAY_SCALE_PULSE_WIDTH);
    for (i = 1; i < 63; i++)
    {
        SPISW_LCD_dat(GAMMA_TABLE[i]);
    }
    SPISW_LCD_cmd(C_SET_RESET_PRECHARGE_PERIOD);
    SPISW_LCD_dat(0x32);
    SPISW_LCD_cmd(C_ENHANCE_DRIVING_SCHEME_CAPABILITY);
    SPISW_LCD_dat(0xa4);
    SPISW_LCD_dat(0x00);
    SPISW_LCD_dat(0x00);
    SPISW_LCD_cmd(C_SET_PRECHARGE_VOLTAGE);
    SPISW_LCD_dat(0x17);
    SPISW_LCD_cmd(C_SET_SECOND_PRECHARGE_PERIOD);
    SPISW_LCD_dat(0x01);
    SPISW_LCD_cmd(C_SET_VCOMH_VOLTAGE);
    SPISW_LCD_dat(0x05);
    SPISW_LCD_cmd(C_SET_DISPLAY_MODE_RESET);
    SPISW_LCD_on();
    delay_ms(100000);
    SPISW_LCD_cmd(C_SET_SLEEP_MODE_OFF);
}

// color = 12-bit color value rrrrggggbbbb
void MARY_OLED_Pat(UINT16 color)
{
    UINT32 x;
    UINT8 h, l;
    h = color >> 8;
    l = color & 0xff;
    SPISW_LCD_cmd(C_SET_ROW_ADDRESS);
    SPISW_LCD_dat(0);
    SPISW_LCD_dat(MARY_OLED_PWX-1);
    SPISW_LCD_cmd(C_SET_COLUMN_ADDRESS);
    SPISW_LCD_dat(0);
    SPISW_LCD_dat(MARY_OLED_PWY-1);
    SPISW_LCD_cmd(C_WRITE_RAM_COMMAND);
    for (x = 0; x < MARY_OLED_PWX * MARY_OLED_PWY; x++){
        SPISW_LCD_dat(h);
        SPISW_LCD_dat(l);
    }
}

void MARY_OLED_Clear()
{
    MARY_OLED_Pat(0);
}

// RGB 565 format
// rrrrrggg gggbbbbb

void MARY_OLED_BitBltEx565(int x, int y, int width, int height, UINT32 data[])
{
    int i, j;
    UINT8 l, h;
    UINT8 *pdata = (UINT8 *)data;
    for (j = 0; j < height; j ++) {
        SPISW_LCD_cmd(C_SET_ROW_ADDRESS);
        SPISW_LCD_dat(y + j);
        SPISW_LCD_dat(y + j + 1);
        SPISW_LCD_cmd(C_SET_COLUMN_ADDRESS);
        SPISW_LCD_dat(x);
        SPISW_LCD_dat(x + width - 1);
        SPISW_LCD_cmd(C_WRITE_RAM_COMMAND);
        for (i = 0; i < width; i ++) {
            l = *pdata++;
            h = *pdata++;
            SPISW_LCD_dat(h);
            SPISW_LCD_dat(l);
        }
    }
}

#ifdef SCROLL
void MARY_OLED_Scroll(UINT8 sy)
{
    SPISW_LCD_cmd(ASCSET);
    SPISW_LCD_dat(SCROLL_SY);
    SPISW_LCD_dat(SCROLL_EY);
    SPISW_LCD_dat(SCROLL_WY);
    SPISW_LCD_dat(SCROLL_FLAG);
    SPISW_LCD_cmd(SCSTART);
    SPISW_LCD_dat(sy);
}
#endif

void MARY_OLED_WriteChar_Color(unsigned char c, UINT8 cx, UINT8 cy, UINT16 fgcol, UINT16 bgcol)
{
    UINT8 x, y;
    UINT16    col;

    char *font = &font6x8[(c & 0x00ff) << 3];
    for (y = 0; y < FONT_WY; y++) {
        SPISW_LCD_cmd(C_SET_ROW_ADDRESS);
        SPISW_LCD_dat(cy * FONT_WY + y + TEXT_SY);
        SPISW_LCD_dat(MARY_OLED_WY - 1);
        SPISW_LCD_cmd(C_SET_COLUMN_ADDRESS);
        SPISW_LCD_dat(cx * FONT_WX + TEXT_SX);
        SPISW_LCD_dat(MARY_OLED_WX - 1);
        SPISW_LCD_cmd(C_WRITE_RAM_COMMAND);
        for (x = 0; x < FONT_WX; x++) {
            if (font[y] & (0x80 >> (x & 0xf))) {
                col = fgcol;
            } else {
                col = bgcol;
            }
            SPISW_LCD_dat(col >> 8);
            SPISW_LCD_dat(col);
        }
    }
}

void MARY_OLED_WriteChar(unsigned char c, int cx, int cy)
{
    MARY_OLED_WriteChar_Color(c, (UINT8)cx, (UINT8)cy, MARY_OLED_FCOL, MARY_OLED_BCOL);
}

void MARY_OLED_WriteFormattedChar(unsigned char ch)
{
    if (ch == 0xc) {
        MARY_OLED_Clear();
        cx = 0;
        cy = 0;
    } else if (ch == '\n') {
        cy++;
        if (cy == MARY_OLED_WY/FONT_WY) {
#ifdef SCROLL
            scroll = true;
#endif
            cy = 0;
        }
#if SCROLL
        if (scroll) {
            sy += SCROLL_WY;
            if (sy == SCROLL_EY)
                sy = SCROLL_SY;
            MARY_OLED_Scroll(sy);
        }
#endif
    } else if (ch == '\r') {
        cx = 0;
    } else {
        MARY_OLED_WriteChar(ch, cx, cy);
        cx ++;
        if (cx == MARY_OLED_WX/FONT_WX) {
            cx = 0;
            cy ++;
            if (cy == MARY_OLED_WY/FONT_WY) {
#ifdef SCROLL
            scroll = true;
#endif
                cy = 0;
            }
#if SCROLL
            if (scroll) {
                sy += SCROLL_WY;
                if (sy == SCROLL_SY)
                    sy = SCROLL_EY;
                MARY_OLED_Scroll(sy);
            }
#endif
        }
    }
}

void LCD_Clear()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    cx = 0;
    cy = 0;
#ifdef SCROLL
    sy = 0;
    scroll = false;
#endif
    MARY_OLED_Clear();
}

BOOL LCD_Initialize()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    MARY_OLED_Initialize();
    LCD_Clear();
    return true;
}

BOOL LCD_Uninitialize()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return true;
}

void LCD_BitBltEx(int x, int y, int width, int height, UINT32 data[])
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    MARY_OLED_BitBltEx565(x, y, width, height, data);
}

void LCD_BitBltEx565(int x, int y, int width, int height, UINT32 data[])
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    MARY_OLED_BitBltEx565(x, y, width, height, data);
}

void LCD_BitBlt(int width, int height, int widthInWords, UINT32 data[], BOOL fUseDelta)
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    MARY_OLED_BitBltEx565(0, 0, width, height, data);
}

void LCD_WriteChar(unsigned char c, int row, int col)
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    MARY_OLED_WriteChar(c, row, col);
}

void LCD_WriteFormattedChar(unsigned char c)
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    MARY_OLED_WriteFormattedChar(c);
}

void LCD_WriteFormattedChar16(unsigned short s)
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    MARY_OLED_WriteFormattedChar((unsigned char)s);
}

INT32 LCD_GetWidth()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return MARY_OLED_WX;
}

INT32 LCD_GetHeight()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return MARY_OLED_WY;
}

INT32 LCD_GetBitsPerPixel()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return MARY_OLED_BITSPERPIXEL;
}

UINT32 LCD_GetPixelClockDivider()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return 0;
}
INT32 LCD_GetOrientation()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return 0;
}

void LCD_PowerSave(BOOL On)
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
}

UINT32 *LCD_GetFrameBuffer()
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return NULL;
}

UINT32 LCD_ConvertColor(UINT32 color)
{
//    NATIVE_PROFILE_HAL_DRIVERS_DISPLAY();
    return color;
}

//--//
